<html>
	<head>
		<title>WebGL SubD</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

		<style>
			body {
				background:#fff;
				color: #333;
				padding:0;
				margin:0;
				font-weight:bold;
				overflow:hidden;

				font-family:Monospace;
				font-size:13px;
				text-align:center;
			}

			#info {
				position: absolute;
				top: 0px; width: 100%;
				padding: 5px;
				z-index:100;
			}

			a { color: #181; }
			a.fileop { color: #bb1; }

			b { color:orange }

			canvas { width: 100%; height: 100% }
		</style>

		<script src="three.js"></script>
		<script src="OrbitControls.js"></script>
		<script src="subd.js"></script>
		<script src="//cdnjs.cloudflare.com/ajax/libs/json2/20121008/json2.min.js"></script>
	</head>
	<body>
		<div id="info"><a href="http://wadeb.com/">wadeb</a> - SubD<br/><br/>
		<b>0-3</b> smoothness, <b>C</b> control mesh, <b>V</b> verts, <b>W</b> wireframe<br/>
		<b>Models [</b> 
		<a href="javascript:loadServerModel('torus.obj');">torus</a> 
		<a href="javascript:loadServerModel('sphere.obj');">sphere</a> 
		<a href="javascript:loadServerModel('cylinder.obj');">cylinder</a> 
		<a href="javascript:loadServerModel('circle.obj');">circle</a> 
		<a href="javascript:loadServerModel('head.obj');">head</a> 
		<a href="javascript:loadServerModel('bigguy_0.obj');">bigguy</a> 
		<a href="javascript:loadServerModel('monsterfrog_cage0.obj');">monsterfrog</a> 
		<a href="javascript:loadServerModel('mudbox_reptile.obj');">reptile</a> 
		<input type="file" id="import" style="display:none;"/>
		<a href="javascript:document.getElementById('import').click();" class="fileop">import.obj</a> 
		<b>]</b><br/>
		<div id="credit">bigguy and monsterfrog created by bay raitt<br/>
		</div>

		<script>
		/**
		 * @author wadeb / http://wadeb.com/
		 */

		function roundRect(ctx, x, y, w, h, r) 
		{
		    ctx.beginPath();
		    ctx.moveTo(x+r, y);
		    ctx.lineTo(x+w-r, y);
		    ctx.quadraticCurveTo(x+w, y, x+w, y+r);
		    ctx.lineTo(x+w, y+h-r);
		    ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
		    ctx.lineTo(x+r, y+h);
		    ctx.quadraticCurveTo(x, y+h, x, y+h-r);
		    ctx.lineTo(x, y+r);
		    ctx.quadraticCurveTo(x, y, x+r, y);
		    ctx.closePath();
		    ctx.fill();
			ctx.stroke();   
		}

		function makeTextSpriteMaterial(message, parameters)
		{
			parameters = parameters || {};
			var backgroundStyle = parameters["backgroundStyle"] || "rgba(255, 255, 255, 1.0)";

			var size = 128;

			var canvas = document.createElement('canvas');
			canvas.width = size;
			canvas.height = size;

			var context = canvas.getContext('2d');

			var fontSize = 100;		
			context.font = "Bold 100pt Arial";

			var metrics = context.measureText(message);

			var width = metrics.width;
			var height = fontSize;

			var padding = 4;			
			var borderThickness = 8;			

			context.strokeStyle = "rgba(0, 0, 0, 1.0)";
			context.fillStyle = backgroundStyle;
			context.lineWidth = borderThickness;

			roundRect(context, 
				size/2 - width/2 - padding - borderThickness/2, 
				size/2 - height/2 - padding - borderThickness/2, 
				width + padding*2 + borderThickness, 
				height + padding*2 + borderThickness, 
				16);
			
			context.fillStyle = "rgba(0, 0, 0, 1.0)";
			context.fillText(message, size/2 - width/2, size/2 + fontSize/2);
			
			var texture = new THREE.Texture(canvas);
			texture.needsUpdate = true;

			var material = new THREE.SpriteMaterial({ 
				map: texture,
				uvOffset: new THREE.Vector2(-0.5, -0.5),
				depthTest: true,
				alphaTest: 0.5
			});

			return material;
		}

		function makeTextSprite(size, material) {
			var sprite = new THREE.Sprite(material);
			sprite.scale = new THREE.Vector3(size, size, size);
			return sprite;
		}

		var vertKindSpriteMaterial = [
			makeTextSpriteMaterial("F", { backgroundStyle: "rgb(0,0,255)" }),
			makeTextSpriteMaterial("E", { backgroundStyle: "rgb(0,255,0)" }),
			makeTextSpriteMaterial("E", { backgroundStyle: "rgb(0,255,0)" }),
			makeTextSpriteMaterial("V", { backgroundStyle: "rgb(255,0,0)" }),
			makeTextSpriteMaterial("V", { backgroundStyle: "rgb(255,0,0)" }),
			makeTextSpriteMaterial("V", { backgroundStyle: "rgb(255,0,0)" }),
		]

		var vertKindSize = [
			0.1, 0.1, 0.1, 0.1, 0.1, 0.1
		]

		function fitSubD(subd, size) {
			var min = new THREE.Vector3(1000000, 1000000, 1000000);
			var max = new THREE.Vector3(-1000000, -1000000, -1000000);
			
			for (var i = 0; i < subd.verts.length; i++) {
				min.min(subd.verts[i]);
				max.max(subd.verts[i]);
			}

			var range = new THREE.Vector3().subVectors(max, min);

			var scale;
			// if (range.x > range.y && range.x > range.z)
			// 	scale = size / range.x;
			// else if (range.y > range.x && range.y > range.z)
				scale = size / range.y;
			// else
			// 	scale = size / range.z;

			var offset = new THREE.Vector3(
				(min.x + max.x) * 0.5,
				min.y,
				(min.z + max.z) * 0.5
			);

			for (var i = 0; i < subd.verts.length; i++) {
				subd.verts[i].sub(offset);
				subd.verts[i].multiplyScalar(scale);
			}
		}

		var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);

		var renderer = new THREE.WebGLRenderer({ "antialias": true });
		renderer.setClearColor("antiquewhite", 1);
		renderer.setSize(window.innerWidth, window.innerHeight);
		renderer.shadowMapEnabled = true;
		renderer.shadowMapSoft = true;
		document.body.appendChild(renderer.domElement);

		var cylinderSubD = new THREE.SubD({
			verts: [
				new THREE.Vector3(-1, -1, -1),
				new THREE.Vector3(-1, -1, 1),
				new THREE.Vector3(-1, 1, -1),
				new THREE.Vector3(-1, 1, 1),
				new THREE.Vector3(1, -1, -1),
				new THREE.Vector3(1, -1, 1),
				new THREE.Vector3(1, 1, -1),
				new THREE.Vector3(1, 1, 1)
			],
			faces: [
				[ 0, 1, 3, 2 ],
				[ 4, 0, 2, 6 ],
				[ 5, 4, 6, 7 ],
				[ 1, 5, 7, 3 ],
			]
		});

		var tetrahedronSubD = new THREE.SubD({
			verts: [
				new THREE.Vector3(-2, -2, -2),
				new THREE.Vector3(2, 2, -2),
				new THREE.Vector3(-2, 2, 2),
				new THREE.Vector3(2, -2, 2),
			],
			faces: [
				[ 1, 2, 3 ],
				[ 0, 3, 2 ],
				[ 0, 2, 1 ],
				[ 0, 1, 3 ],
			]
		});

		var manager = new THREE.LoadingManager();
		manager.onProgress = function ( item, loaded, total ) {
			console.log( item, loaded, total );
		};

		// var texture = new THREE.Texture();
		// var loader = new THREE.ImageLoader( manager );
		// loader.load( 'textures/UV_Grid_Sm.jpg', function ( image ) {
		// 	texture.image = image;
		// 	texture.needsUpdate = true;
		// } );

		var sceneScale = 4.0;
		var model;

		function processModel(subd) {
			fitSubD(subd, sceneScale)
			model = subd;
			buildGeometry();
			buildScene();
			render();
		}

		function loadServerModel(url) {
			var loader = new THREE.SubDOBJLoader(manager);
			loader.load(url, function(subd) {
				processModel(subd);
			});			
		}

		function loadLocalModel(event) {
			var reader = new FileReader();

			reader.onload = function(e) {
				var loader = new THREE.SubDOBJLoader(manager);
				var subd = loader.parse(e.target.result);
				processModel(subd);
			}

			reader.readAsText(event.target.files[0]);
		}

		document.getElementById('import').addEventListener('change', loadLocalModel, false);

		var subdMaterial = new THREE.MeshPhongMaterial( { ambient: 0xffffff, color: 0xdddddd, specular: 0 } );
		var subdInsideMaterial = new THREE.MeshPhongMaterial( { ambient: 0x303030, color: 0xdddddd, specular: 0 } );

		var subdWireMaterial = new THREE.LineBasicMaterial({ color: 0x404040 });
		var controlWireMaterial = new THREE.LineBasicMaterial({ color: 0x008000 });

		var enableControlGeometry = true;
		var enableWireframe = true;
		var enableVerts = false;
		var vertShrink = 0;
		var enableShadows = false;
		var enablePlane = false;
		var smoothLevel = 3;

		var scene = null;

		var subdMesh;
		var controlWireGeometry;
		var subdGeometry;
		var subdInsideGeometry;
		var subdWireGeometry;

		var buildGeometry = function() {
			if (model) {
				controlWireGeometry = model.makeWireGeometry();

				subdMesh = model;
				for (var i = 0; i < smoothLevel; i++)
					subdMesh = subdMesh.smooth();

				subdMesh.calculateNormals();

				subdGeometry = subdMesh.makeGeometry();
				subdInsideGeometry = subdMesh.makeGeometry({ flipWinding: true });
				subdWireGeometry = subdMesh.makeWireGeometry();
			}
		}

		var buildScene = function() {
			scene = new THREE.Scene();

			var light = new THREE.AmbientLight(0x808080);
			scene.add(light);

			if (0) {
				var light = new THREE.SpotLight(0xe0e0e0);
				light.position.set(5, 5, 5);
				light.target.position.set( 0, 0, 0 );
				if (enableShadows) {
					light.castShadow = true;
					light.shadowCameraNear = 6;
					light.shadowCameraFar = 10;
					light.shadowCameraFov = 30;
					light.shadowDarkness = 1;
					light.shadowCameraVisible = true;
					light.shadowMapWidth = 1024;
					light.shadowMapHeight = 1024;
				}
				scene.add(light);
			} else {
				var light = new THREE.DirectionalLight(0xe0e0e0, 0.5);
				light.position.set(10, 10, 10);
				light.target.position.set( 0, 0, 0 );
				if (enableShadows) {
					light.castShadow = true;
					light.shadowCameraNear = 10;
					light.shadowCameraFar = 30;
					light.shadowCameraRight = 10;
					light.shadowCameraLeft = -10;
					light.shadowCameraTop = -10;
					light.shadowCameraBottom = 10;
					light.shadowDarkness = 0.5;
					light.shadowCameraVisible = true;
					light.shadowMapWidth = 2048;
					light.shadowMapHeight = 2048;
				}
				scene.add(light);
			}

			if (model) {
				if (enableControlGeometry) {
					var controlWireObject = new THREE.Line(controlWireGeometry, controlWireMaterial, THREE.LinePieces);
					scene.add(controlWireObject);
				}

				var subdObject = new THREE.Mesh(subdGeometry, subdMaterial);
				if (enableShadows) {
					subdObject.castShadow = true;
					subdObject.receiveShadow = false;
				}
				scene.add(subdObject);

				var subdInsideObject = new THREE.Mesh(subdInsideGeometry, subdInsideMaterial);
				if (enableShadows) {
					subdInsideObject.castShadow = true;
					subdInsideObject.receiveShadow = true;
				}
				scene.add(subdInsideObject);

				if (enableVerts) {
					for (var i = 0; i < subdMesh.verts.length; i++) {
						var vert = subdMesh.verts[i];
						var kind = subdMesh.vertKinds[i];
						var size = vertKindSize[kind] / (1.0 + smoothLevel*vertShrink);
						var sprite = makeTextSprite(size, vertKindSpriteMaterial[kind]);
						sprite.position = vert;
						scene.add(sprite);
					}
				}

				if (enableWireframe) {
					var subdWireObject = new THREE.Line(subdWireGeometry, subdWireMaterial, THREE.LinePieces);
					scene.add(subdWireObject);
				}
			}

			if (enablePlane) {
				var plane = new THREE.Mesh(new THREE.PlaneGeometry(10, 10, 1, 1), subdMaterial);
				plane.rotateOnAxis(new THREE.Vector3(1, 0, 0), -3.14159/2);
				if (enableShadows) {
					plane.receiveShadow = true;
				}
				scene.add(plane);
			}
		}

		var render = function() {
			if (scene) 
				renderer.render(scene, camera);
		}

		controls = new THREE.OrbitControls(camera);
		controls.zoomSpeed = 0.5;
		controls.update();
		controls.addEventListener('change', render);

		var onKeyDown = function(event) {
			switch( event.keyCode ) {
				case 48: /*0*/ smoothLevel = 0; buildGeometry(); break;
				case 49: /*1*/ smoothLevel = 1; buildGeometry(); break;
				case 50: /*2*/ smoothLevel = 2; buildGeometry(); break;
				case 51: /*3*/ smoothLevel = 3; buildGeometry(); break;
				//case 52: /*4*/ smoothLevel = 4; buildGeometry(); break;
				case 67: /*c*/ enableControlGeometry = !enableControlGeometry; break;
				case 83: /*s*/ enableShadows = !enableShadows; break;
				case 86: /*v*/ enableVerts = !enableVerts; break;
				case 87: /*w*/ enableWireframe = !enableWireframe; break;
				default: return;
			}		
			buildScene();
			render();
		}

		window.addEventListener( 'keydown', onKeyDown, false );

		var onWindowResize = function() {
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();

			renderer.setSize(window.innerWidth, window.innerHeight);

			render();
		}

		window.addEventListener( 'resize', onWindowResize, false );

		buildGeometry();
		buildScene();
		render();

		// loadServerModel('monsterfrog_cage0.obj');
		// loadServerModel('circle.obj');
		// camera.position.set(1, 3, 8);
		// controls.target.set(0, 2, 0);
		// controls.update();
		loadServerModel('head.obj');
		camera.position.set(1, 3, 8);
		controls.target.set(0, 2, 0);
		controls.update();
		</script>
	</body>
</html>
